﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

using FarseerPhysics;
using Animation;
using MapLibrary;
using MapLibrary.Players;
using FarseerPhysics.Dynamics;
using Enemies;
using MapLibrary.Particles;

namespace AI
{
    public class TuberculosisBot : Bot
    {
        /// <summary>
        /// Enum for all states the bot can be in
        /// </summary>
        private enum State
        {
            RUNNING_RIGHT,
            RUNNING_LEFT,
            IDLE,
            ATTACKING_RIGHT,
            ATTACKING_LEFT,
            FLEEING_RIGHT,
            FLEEING_LEFT
        }

        /// <summary>
        /// max distance the bot can attack the player
        /// </summary>
        const float ATTACK_DISTANCE_SQUARED = 20f;

        const float V_X = 3.0f;

        /// <summary>
        /// number of frames the bot must wait between each attack
        /// </summary>
        const int ATTACK_COOLDOWN = 100;

        /// <summary>
        /// number of frames for the attack animation
        /// </summary>
        const int ATTACK_FRAMES = 60;

        /// <summary>
        /// stack for the bots projectiles
        /// </summary>
        private Stack<Sprite> ammo;

        /// <summary>
        /// states the bot is in
        /// </summary>
        private State currentState;

        /// <summary>
        /// frames left until the bot can attack
        /// </summary>
        private int timeToAttack;

        /// <summary>
        /// frames left until the next update
        /// </summary>
        private int timeToUpdate;

        public TuberculosisBot(Sprite owner)
            : base(owner)
        {
        }

        public TuberculosisBot()
        {
        }

        /// <summary>
        /// put a used projectile back into the ammo stack
        /// </summary>
        /// <param name="s"></param>
        public void RecycleAmmo(Sprite s)
        {
            if (!ammo.Contains(s))
            {
                ammo.Push(s);
            }
        }

        /// <summary>
        /// initialize all needed variables and fill the ammo stack
        /// </summary>
        /// <param name="world"></param>
        /// <param name="cm"></param>
        public override void Init(GameWorld world, ContentManager cm)
        {
            currentState = State.IDLE;
            timeToAttack = ATTACK_COOLDOWN;
            timeToUpdate = 60;

            ammo = new Stack<Sprite>();
            SpriteType st = cm.Load<SpriteType>(@"Animation/SpriteTypes/TuberculosisSpit");

            for (int i = 0; i < 10; i++)
            {
                Sprite s = SpriteFactory.CreateSprite(world,st);
                TuberculosisSpit ts = s as TuberculosisSpit;
                ts.Owner = Owner;
                s.Body.Enabled = false;
                s.Body.IgnoreGravity = true;
                s.Body.IsBullet = true;
                s.Body.FixedRotation = false;
                ammo.Push(s);
            }
        }


        public override void Update(Level level, World world)
        {
            Player p = level.Player;
            Vector2 pos = Owner.Body.Position;
            Vector2 playerPos = p.Body.Position;

            float dx = pos.X - playerPos.X;
            float dy = pos.Y - playerPos.Y;

            timeToAttack--;
            timeToUpdate--;

            Vector2 viewportPos = Owner.World.Viewport.WorldToViewportCoords(Owner.Position);

            if (!Owner.World.Viewport.IsWorldPointInViewport(Owner.Position))
                return;

            if (timeToAttack <= 0 && currentState != State.FLEEING_LEFT && currentState != State.FLEEING_RIGHT)
            {
                float distSquared = dx * dx + dy * dy;

                if (distSquared <= ATTACK_DISTANCE_SQUARED)
                {
                    timeToAttack = ATTACK_COOLDOWN;
                    if (ammo.Count > 0)
                    {
                        Vector2 startPos;
                        if (pos.X > playerPos.X)
                        {
                            currentState = State.ATTACKING_LEFT;
                            startPos = Owner.Body.Position;
                        }
                        else
                        {
                            currentState = State.ATTACKING_RIGHT;
                            startPos = Owner.Body.Position + new Vector2(ConvertUnits.ToSimUnits(Owner.CurrentTexture.Bounds.Width), 0);
                        }
                        Vector2 bulletVel = new Vector2(ConvertUnits.ToSimUnits(130), ConvertUnits.ToSimUnits(130));

                        tVec.X = playerPos.X - pos.X;
                        tVec.Y = playerPos.Y - pos.Y;
                        tVec.Normalize();
                        tVec *= bulletVel;

                        Sprite s = ammo.Pop();
                        s.Body.LinearVelocity = Vector2.Zero;
                        level.AddSprite(s);
                        level.AddBodyToEnable(s);

                        startPos.Y += ConvertUnits.ToSimUnits(3);
                        s.Body.Position = startPos;

                        s.Body.ApplyLinearImpulse(ref tVec);

                        timeToUpdate = ATTACK_FRAMES;
                    }
                }
            }
            else
            {
                //move toward the player modulo a buffer zone
                //only if the player is on the screen.

                float pBufferZone = 2.0f; //1 meter buffer
                float sideBufferZone = 3.0f;

                //only move if done shooting animation!
                if (Owner.Type.CurrentState.Contains("Attack"))
                {
                    if (!Owner.Type.States[Owner.Type.CurrentState].IsStateComplete())
                        return;
                }

                if (Math.Abs(dx) < pBufferZone)
                {
                    currentState = State.IDLE;
                }
                else
                {
                    //player is to the right
                    if (dx < 0)
                    {
                        if (!NearLedgeRight(sideBufferZone))
                        {
                            if(!Owner.Type.CurrentState.Equals("MoveRight"))
                                Owner.Type.CurrentState = "MoveRight";
                            currentState = State.RUNNING_RIGHT;
                            if(Owner.Body.LinearVelocity.X < 1.0f)
                                Owner.Body.ApplyLinearImpulse(new Vector2(0.1f, 0.0f));
                        }
                    }
                    else if (dx > 0)
                    {
                        if (!NearLedgeLeft(sideBufferZone))
                        {
                            if(!Owner.Type.CurrentState.Equals("MoveLeft"))
                                Owner.Type.CurrentState = "MoveLeft";
                            currentState = State.RUNNING_LEFT;
                            if(Owner.Body.LinearVelocity.X > -1.0f)
                                Owner.Body.ApplyLinearImpulse(new Vector2(-0.1f, 0.0f));
                        }
                    }
                }
            }

        }
    }
}
